package gbench.appdemo.crawler.srch;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TotalHits;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import gbench.common.tree.LittleTree.IRecord;
import static gbench.common.tree.LittleTree.IRecord.*;

/**
 * 这是从Luke 拷贝过来的代码
 * Holder for a search result page.
 */
public final class PageData {

  private TotalHits totalHits;

  private int offset = 0;

  private List<Doc> hits = new ArrayList<>();

  /**
   * Creates a search result page for the given raw Lucene hits.
   *
   * @param totalHits - total number of hits for this query
   * @param docs - array of hits
   * @param offset - offset of the current page
   * @param searcher - index searcher
   * @param fieldsToLoad - fields to load
   * @return the search result page
   * @throws IOException
   */
  static PageData of(TotalHits totalHits, ScoreDoc[] docs, int offset,
                          IndexSearcher searcher, Set<String> fieldsToLoad)
      throws IOException {
    PageData res = new PageData();

    res.totalHits = Objects.requireNonNull(totalHits);
    Objects.requireNonNull(docs);
    Objects.requireNonNull(searcher);

    for (ScoreDoc sd : docs) {
      Document luceneDoc = (fieldsToLoad == null) ?
          searcher.doc(sd.doc) : searcher.doc(sd.doc, fieldsToLoad);
      res.hits.add(Doc.of(sd.doc, sd.score, luceneDoc));
      res.offset = offset;
    }

    return res;
  }

  /**
   * Returns the total number of hits for this query.
   */
  public TotalHits getTotalHits() {
    return totalHits;
  }

  /**
   * Returns the offset of the current page.
   */
  public int getOffset() {
    return offset;
  }

  /**
   * Returns the documents of the current page.
   */
  public List<Doc> getHits() {
    return Collections.unmodifiableList(hits);
  }
  
  /**
   * Returns the documents of the current page.
   */
  public Stream<IRecord> hitsStream() {
    return this.getHits().stream().map(Doc::record);
  }
  
  /**
   * 遍历查询的当前页面中结果数据
   * @param cs hits 的遍历函数
   */
  public void forEach(Consumer<IRecord>cs) {
    this.hitsStream().forEach(cs);
  }
  
  /**
   * 遍历查询的当前页面中结果数据
   * @param cs hits 的遍历函数
   */
  public void forEach2(Consumer<Doc>cs) {
    this.getHits().forEach(cs);
  }

  /**
   * Returns the size of the current page.
   */
  public int size() {
    return hits.size();
  }

  /**
   * 构造函数
   */
  private PageData() {
  }

  /**
   * Holder for a hit.
   */
  public static class Doc {
    private int docId;
    private float score;
    private Map<String, String[]> fieldValues = new HashMap<>();

    /**
     * Creates a hit.
     *
     * @param docId - document id
     * @param score - score of this document for the query
     * @param luceneDoc - raw Lucene document
     * @return the hit
     */
    static Doc of(int docId, float score, Document luceneDoc) {
      Objects.requireNonNull(luceneDoc);

      Doc doc = new Doc();
      doc.docId = docId;
      doc.score = score;
      Set<String> fields = luceneDoc.getFields().stream().map(IndexableField::name).collect(Collectors.toSet());
      for (String f : fields) {
        doc.fieldValues.put(f, luceneDoc.getValues(f));
      }
      return doc;
    }
    
    /**
     * 转换成文档记录:对于docid,和score采用
     * 
     *  @param prefix 对于 Doc 字段采用prefix指定的前缀进行区别，当 设为null 默认为 "",表示不进行却别
     * @return IRecord
     */
    public IRecord record(final String prefix) {
        final var final_prefix = (prefix == null?"":prefix);
        final var rec = REC("docid",final_prefix+docId,"score",final_prefix+score);// 基础Doc 成员。
        return rec.derive(fldvals());
    }
    
    /**
     * 把fieldValues 转换成 IRecord 记录 
     * 
     * @return IRecord
     */
    public IRecord fldvals() {
        final var rec = REC();// 基础Doc 成员。
        fieldValues.forEach((k,v)->{
            if(v.length<2) {
                rec.add(k,v[0]);
            }else {
                rec.add(k,v);
            }//if
        });//forEach
        return rec;
    }
    
    /**
     * 转换成文档记录:对于docid,和score采用
     * 对于 Doc 字段采用默认前缀，即不带有前缀。不进行却别
     * @return IRecord
     */
    public IRecord record() {
        return record(null);
    }

    /**
     * Returns the document id.
     */
    public int getDocId() {
      return docId;
    }

    /**
     * Returns the score of this document for the current query.
     */
    public float getScore() {
      return score;
    }

    /**
     * Returns the field data of this document.
     */
    public Map<String, String[]> getFieldValues() {
      return Collections.unmodifiableMap(fieldValues);
    }

    private Doc() {
    }
  }
}